<template>
  <div
    class="vxe-commn-container-pro"
    v-loading="tableLoading"
    :id="`vxe-${id}`"
    :class="{ full: showScrennfull ? isFull : '', 'alone-page': alone }"
  >
    <SearchForm
      class="shanshu-protable-form"
      ref="searchForm"
      :tableRef="tableRef"
      :formOptions="formOptions"
      :updateSearchParams="updateSearchParams"
      :getTableList="getTableList"
      :reset="reset"
      :search="search"
    >
      <template v-for="(_, slotName) in $slots" :key="slotName" #[slotName]="scope">
        <slot :name="slotName" v-bind="scope"></slot>
      </template>
    </SearchForm>
    <el-divider v-if="divider" style="margin: 0 0 10px 0" />
    <Toolbar
      ref="toolbarRefChild"
      :tableRef="tableRef"
      :exportHandler="exportHandler"
      :screenfullHandler="screenfullHandler"
      :tableData="tableData"
      :isFull="isFull"
      :changeButtonsVisible="changeButtonsVisible"
      v-bind="$attrs"
      :showScrennfull="showScrennfull"
      v-if="showToolbar"
    >
      <template v-for="(_, slotName) in $slots" :key="slotName" #[slotName]="scope">
        <slot :name="slotName" v-bind="scope"></slot>
      </template>
    </Toolbar>
    <div class="table-container" :class="size === 'mini' ? '' : 'min-height'">
      <vxe-table
        show-overflow="title"
        show-header-overflow="title"
        keep-source
        :custom-config="customConfig"
        @checkbox-all="selectAllChangeEvent"
        @checkbox-change="selectChangeEvent"
        :id="id"
        :key="tableKey"
        auto-resize
        style="width: 100%; z-index: 1"
        ref="tableRef"
        height="100%"
        v-loading="loading"
        :stripe="stripe"
        :edit-config="editConfig"
        border="none"
        :sort-config="sortConfig"
        @sort-change="sortChangeEvent"
        :validConfig="{ msgMode: 'full' }"
        :data="tableData"
        :size="size"
        :row-config="{ isHover: true }"
        :column-config="{ resizable: true }"
        :resizable-config="{ showDragTip: false }"
        row-class-name="shanshu-protable-row"
        v-bind="$attrs"
      >
        <vxe-column
          v-for="(item, index) in columns"
          :key="index"
          :edit-render="renderEidtConfig(item)"
          :min-width="item.field ? 100 : 50"
          :sortable="item.sortable ?? true"
          class-name="cell-render-name"
          v-bind="item"
          :fixed="item.fixed ? item.fixed : item.type === 'checkbox' ? 'left' : ''"
        >
          <template #default="{ row }" v-if="item.slotName || item.showType">
            <slot v-if="item.slotName" :name="item.slotName" :row="row"></slot>
            <template v-else-if="item.showType === 'time'">{{
              formatDate(row[item.field])
            }}</template>
            <TextRender
              v-else-if="item.showType === 'text'"
              :text="row[item.field] ?? ''"
              :textColor="row[item.field + 'Color'] || ''"
            />
            <template v-else>{{ row[item.field] }}</template>
          </template>
          <template v-if="item.editSlotName" #edit="{ row }">
            <slot :name="item.editSlotName" :row="row"></slot>
          </template>
        </vxe-column>
        <vxe-column
          title="操作"
          fixed="right"
          field="action"
          show-overflow
          align="center"
          v-bind="actionsParams"
          :resizable="false"
          :width="showActions ? (actionsParams.width ?? 90) : 40"
          class-name="action-cell-render"
          v-if="showActions"
        >
          <template #default="{ row }">
            <slot name="actionBtnSlot" :row="row"></slot>
            <template v-if="!$slots.actionBtnSlot">
              <template v-if="hasEditStatus(row)">
                <el-tooltip :show-after="100" content="保存" placement="top">
                  <el-button link @click="saveRowEvent(row)" class="shanshu-btn-hover"
                    ><Icon icon="svg-icon:icon-hua-save" color="#8F919E"
                  /></el-button>
                </el-tooltip>
                <el-tooltip :show-after="100" content="取消" placement="top">
                  <el-button link @click="cancelRowEvent(row)" class="shanshu-btn-hover"
                    ><Icon icon="svg-icon:icon-hua-cancel" color="#8F919E"
                  /></el-button>
                </el-tooltip>
              </template>
              <template v-else>
                <el-tooltip
                  v-if="actionsPermi.edit"
                  :show-after="100"
                  placement="top"
                  content="编辑"
                >
                  <el-button @click="editRowEvent(row)" link class="shanshu-btn-hover"
                    ><Icon icon="svg-icon:icon-hua-edit" color="#082050"
                  /></el-button>
                </el-tooltip>
                <el-tooltip
                  v-if="actionsPermi.del"
                  :show-after="100"
                  content="删除"
                  placement="top"
                >
                  <el-button @click="deleteRowEvent(row)" link class="shanshu-btn-hover"
                    ><Icon icon="svg-icon:icon-hua-delete" color="#082050"
                  /></el-button>
                </el-tooltip>
              </template>
            </template>
          </template>
        </vxe-column>
        <template #empty>
          <NoDataContent />
        </template>
      </vxe-table>
    </div>
    <div v-if="pagination" class="shanshu-protable-pagination">
      <VxePagination
        v-model:page="pageable.pageNo"
        v-model:limit="pageable.pageSize"
        :total="pageable.total"
        @pagination="pageChange"
      />
      <!-- <Pagination
        v-else
        size="small"
        v-model:page="pageable.pageNo"
        v-model:limit="pageable.pageSize"
        :total="pageable.total"
        @pagination="pageChange"
      /> -->
    </div>
    <Modal ref="modalRef" />
  </div>
</template>

<script lang="ts" setup>
import { ref, onMounted } from 'vue'
import { TableProps } from 'element-plus'
import { VxeUI, VxeTableInstance, VxeTableEvents, VxeTablePropTypes } from 'vxe-table'
import { useTable } from './hooks/useTable'
import Toolbar from './components/Toolbar/index.vue'
import SearchForm from './components/SearchForm/index.vue'
import Modal from './components/Modal/index.vue'
import { formatDate } from '@/utils/formatTime'
import * as BaseApi from '@/api/base/index'
import screenfull from 'screenfull'
import TextRender from '@/commn/TextRender/index.vue'
import NoDataContent from '@/commn/NoDataContent/index.vue'

import to from 'await-to-js'
import { exportFunc } from '@/utils/common'
interface ProTableProps extends Partial<Omit<TableProps<any>, 'data'>> {
  actionsPermi?: { del: boolean; edit: boolean } // 操作中的 按钮权限。 包含  编辑 和 删除， 不传 默认有权限
  requestApi: (res: any) => Promise<any>
  exportApi?: (res: any) => any // 导出方法的 api
  exportTitle?: string // 导出文件名
  columns: any
  formOptions?: any
  actionsParams?: any // 操作栏配置项
  dataCallback?: (data: any) => any // 返回数据的回调函数，可以对数据进行处理 ==> 非必传
  initParam?: any // 默认查询参数
  pagination?: boolean // 是否需要分页组件 ==> 非必传（默认为true）
  batchEditHandler?: Function // 打开批量编辑的时候触发， 父组件使用的时候可以修改 colums 的配置
  editSuccess?: Function // 当行编辑成功回调
  id?: string // 表格id  用于匹配列设置 和 可编辑项组件状态， 使用列设置功能的组件 id 不要重复
  deleteRow?: Function // 删除当前行数据事件
  restoreCustomStore?: Function // 列设置初始化， https://vxetable.cn/#/component/table/custom/remoteFull
  saveCustomSetting?: Function // 列设置修改以后的存储， 用于后端远程保存， 接受的参数中 type 为 reset 表示重置， confirm 表示确认
  customStr?: string // 如果上面两个没有传，则需要穿列设 置的 key， 然后组件内处理列设置的 后端存储逻辑, 如果不传这个，则隐藏列设置按钮
  showScrennfull?: boolean // 是否使用全屏功能
  defaultActions?: boolean // 是否显示操作栏的参数
  size?: any
  showToolbar?: boolean // 是否需要中间按钮组 及工具栏，增加此参数 避免样式问题发生
  alone?: boolean // 是否是单独的一个页面， 用于区分样式， 在单页面和一个页面多个表格的情况下样式不一样， 样式区别在 alone-page  类名中
  divider?: boolean // 是否展示分割线
  customHandlerParams?: any
  showExportMsg?: boolean // 是否展示导出条数的提示， 默认展示
}
// 🌟组件props的ts定义必须在组件中声明
const props = withDefaults(defineProps<ProTableProps>(), {
  actionsPermi: () => ({ del: true, edit: true }),
  columns: () => [],
  batchEditHandler: () => {},
  id: 'vxeTable',
  restoreCustomStore: undefined,
  saveCustomSetting: undefined,
  editSuccess: () => {},
  deleteRow: () => {},
  pagination: true,
  actionsParams: {},
  initParam: {},
  showScrennfull: true,
  defaultActions: true,
  size: 'medium',
  stripe: true,
  showToolbar: true,
  alone: false,
  divider: false,
  showExportMsg: true
})
const tableKey = ref(1)
const tableRef = ref<VxeTableInstance>()
const tableLoading = ref(false)
const searchForm = ref() // 查询表单 ref
const isFull = ref(false) // 是否全屏
const modalRef = ref()
const toolbarRefChild = ref<any>()
const showActions = ref<boolean>(true) // 是否展示操作栏
const sortConfig = ref<VxeTablePropTypes.SortConfig<any>>({
  remote: true, // 后端排序 开启以后， 前端排序失效  配合 sortChangeEvent 方法使用
  multiple: true, // 后端多列排序 预留
  // 后端排序  使用这个， 前端排序也失效， 通过 return ， 来返回列表数据
  sortMethod: (res) => {
    console.log('后端排序', res)
    return []
  }
})
let selectList = ref<any>([]) // 选中项
const editConfig = ref<any>({
  trigger: 'manual',
  mode: 'row',
  autoClear: false, //  失焦自动触发保存
  showStatus: true,
  showIcon: false // 是否显示表头可编辑状态
}) // 可编辑配置项 一共四项
const beforeRequest = () => {
  selectList.value = []
}
// 表格操作 Hooks
const {
  tableData,
  pageable,
  handleSizeChange,
  loading,
  searchParam,
  reset,
  getTableList,
  search,
  updateSearchParams,
  updateSortParams,
  initTableRef
} = useTable(
  props.requestApi,
  props.initParam,
  props.pagination,
  searchForm,
  beforeRequest,
  props.dataCallback,
  props?.customHandlerParams || false
)
const pageChange = ({ limit: pageSize, page: currentPage }) => {
  handleSizeChange(pageSize, currentPage)
}

const hasEditStatus = (row) => {
  const $table = tableRef.value
  if ($table) {
    return $table.isEditByRow(row)
  }
}

const editRowEvent = (row) => {
  const $table = tableRef.value
  if ($table) {
    const insertLoad = $table.getInsertRecords()
    // 如果有之前编辑的数据，但是没有保存的， 重置其数据。
    if (insertLoad.length !== 0) {
      VxeUI.modal.message({ status: 'error', content: '有新增的数据没有保存!' })
      return
      // $table.revertData(insertLoad[0])
    }
    $table.setEditRow(row)
  }
}
// 删除当前行数据
const deleteRowEvent = (row) => {
  VxeUI.modal.confirm({
    className: 'common-dialog-rest',
    content: '是否确认删除?',
    onConfirm() {
      const $table = tableRef.value
      if ($table) {
        const insertRecords = $table.getInsertRecords() // 获取新增项
        // 使用 row 对比新增项队列， 如果在队列中，证明是新增的， 不是接口请求的， 可以直接删除  可使用 _X_ROW_KEY 字段进行比较
        if (!insertRecords.map((item) => item._X_ROW_KEY).includes(row._X_ROW_KEY)) {
          // 非新增项的删除
          props.deleteRow(row)
          return
        }
        // 新增项的删除
        $table.remove(row)
      }
    }
  })
}
// 保存当前行数据
const saveRowEvent = async (row) => {
  const $table = tableRef.value
  if ($table) {
    const errMap = await $table.validate(row)
    if (errMap) {
      VxeUI.modal.message({ status: 'error', content: '校验不通过！' })
    } else {
      tableLoading.value = true
      const insertRecords = $table.getInsertRecords() // 获取新增项
      // 判断当前保存的行数据 是新增的 还是编辑的
      let addType = 'edit'
      if (insertRecords && insertRecords[0]?._X_ROW_KEY === row._X_ROW_KEY) {
        addType = 'add'
      }
      const result = await props.editSuccess(row, addType)
      tableLoading.value = false
      if (result) {
        VxeUI.modal.message({ status: 'success', content: '保存成功！' })
        getTableList()
        return
      }
    }
  }
}

const cancelRowEvent = (row) => {
  // 取消编辑， 取消的如果是新增项， 直接删除
  const $table = tableRef.value
  if (!$table) return
  const insertRecords = $table.getInsertRecords() // 获取新增项
  if (insertRecords.length !== 0) {
    VxeUI.modal.confirm({
      className: 'common-dialog-rest',
      content: '数据未保存,将删除,是否确认?',
      onConfirm() {
        if ($table) {
          $table.clearValidate() // 清除验证
          $table.clearEdit().then(() => {
            // 删除行数据
            $table.remove(row)
          })
        }
      }
    })
    return
  }
  VxeUI.modal.confirm({
    className: 'common-dialog-rest',
    content: '数据未保存,将不会入库,是否确认?',
    onConfirm() {
      if ($table) {
        $table.clearValidate() // 清除验证
        $table.clearEdit().then(() => {
          // 还原行数据
          $table.revertData(row)
        })
      }
    }
  })
}

// 切换操作栏按钮显示状态 及表格 编辑部分显示
const changeButtonsVisible = () => {
  showActions.value = !showActions.value
  editConfig.value.trigger = showActions.value ? 'manual' : 'click'
  props.columns.map((item) => {
    if (item.type) {
      item.visible = !item?.visible
    }
  })
  props.batchEditHandler()
}

// 列配置  https://vxetable.cn/#/table/api?q=custom-config
const customConfig: any = {
  placement: 'top-left',
  resizable: true,
  trigger: 'click',
  allowFixed: false,
  storage: {
    visible: true,
    resizable: true,
    sort: true
  },
  restoreStore: (params) => {
    if (props.restoreCustomStore) {
      return props.restoreCustomStore(params) // 通过 promise 来确认 是否存储成功 ， 这里通过服务器存储， 这里不需要存
    }
    if (!props.customStr) return
    return new Promise(async (resolve) => {
      tableLoading.value = true
      const [err, result] = await to(BaseApi.getCustomerConfig({ type: props.customStr }))
      tableLoading.value = false
      if (err) resolve({})
      else resolve(JSON.parse(result?.columns || '{}'))
    })
  },
  updateStore: async (params) => {
    // const { type } = params
    tableLoading.value = true
    // if (type === 'update:visible') return (tableLoading.value = false) // 列宽调整也会触发， 这里为防止配置了列宽， 触发方法，直接返回
    if (props.saveCustomSetting) {
      tableLoading.value = false
      return props.saveCustomSetting(params)
    }
    if (!props.customStr) return
    const { storeData } = params
    const [err, result] = await to(
      BaseApi.setCustomerConfig({
        type: props.customStr,
        columns: JSON.stringify(storeData)
      })
    )
    tableLoading.value = false
    // console.log('err', err, result)
  },
  // 操作列 不显, title 为空的 指的是组件控制的 如多选列， 序号这种， 也不显示
  visibleMethod: (res) => {
    const {
      column: { title, type }
    } = res
    if (title === '操作' || type === 'action') return false
    return title
  }
}
const selectAllChangeEvent: VxeTableEvents.CheckboxAll = () => {
  const $table = tableRef.value
  if ($table) {
    const records = $table.getCheckboxRecords()
    selectList.value = records
  }
}

const selectChangeEvent: VxeTableEvents.CheckboxChange = () => {
  const $table = tableRef.value
  if ($table) {
    const records = $table.getCheckboxRecords()
    selectList.value = records
  }
}
// 导出
const exportHandler = () => {
  let count = 0
  let params: any = {}
  if (!props.showExportMsg) {
    params = Object.assign({ ...searchParam.value }, props.initParam)
    modalRef.value.openMsgEvent({
      msg: `是否导出？`,
      async success() {
        if (!props.exportApi) return
        tableLoading.value = true
        const [_err, result] = (await to(props.exportApi(params))) as any
        tableLoading.value = false
        if (!result) return
        exportFunc(result)
      }
    })
    return
  }
  if (pageable.value.total === 0) {
    VxeUI.modal.message({ status: 'error', content: '暂无数据可导出' })
    return
  }
  const $table = tableRef.value
  if (!$table) return
  const insertRecords = $table.getInsertRecords() // 获取新增项
  if (insertRecords.length !== 0) {
    VxeUI.modal.message({ status: 'error', content: '有新增数据未保存，请保存后再导出!' })
    return
  }
  if (selectList.value.length === 0) {
    // 参数项导出
    count = pageable.value.total
    params = Object.assign({ ...searchParam.value }, props.initParam)
  } else {
    // 选中项导出
    count = selectList.value.length
    params = { ...props.initParam }
    params.ids = selectList.value.map((item) => item.id)
  }
  // console.log('searchParam', searchParam.value, selectList.value, params)
  modalRef.value.openMsgEvent({
    msg: `已过滤${count}条数据，是否导出？`,
    async success() {
      if (!props.exportApi) return
      tableLoading.value = true
      const [_err, result] = (await to(props.exportApi(params))) as any
      tableLoading.value = false
      if (!result) return
      exportFunc(result)
    }
  })
}

// 初始化操作栏的按钮权限
if (Object.keys(props.actionsPermi).every((item) => props.actionsPermi[item] === false))
  showActions.value = false
if (props.defaultActions === false) showActions.value = false

// 后端排序 预留 "desc"
const sortChangeEvent = (sort) => {
  const { sortList } = sort
  const orderObj = sortList.reduce((prev, item) => {
    const { property, order } = item
    return {
      ...prev,
      [property]: order === 'asc' ? 1 : 2
    }
  }, {})
  updateSortParams(orderObj)
}

// 可编辑行数据配置， 默认给 input 框
const renderEidtConfig = (item: any) => {
  if (item?.editRender) return { immediate: true, ...item.editRender }
  return {
    name: 'VxeInput',
    immediate: true
  }
}

// 全屏
const screenfullHandler = () => {
  const el = document.getElementById('shanshu-body') as HTMLElement
  if (el) {
    screenfull.toggle(el)
  }
}

onMounted(() => {
  // 渲染中间操作栏的按钮部分
  const $table = tableRef.value
  const $toolbar = toolbarRefChild.value?.toolbarRef
  initTableRef($table)
  if ($table && $toolbar) {
    $table.connect($toolbar)
  }
  if (screenfull.isEnabled) {
    screenfull.on('change', () => {
      isFull.value = screenfull.isFullscreen
    })
  }
})

// searchParam 查询参数 用于导出
defineExpose({ tableData, selectList, getTableList, pageable, searchForm, searchParam })
</script>
<style lang="scss" scoped>
@import './index.scss';
</style>
